#include "ControladorConexiones.h"

#define MENSAJE_ESPERANDO_JUGADORES "Esperando ingreso de jugadores."
#define MENSAJE_INICIO_JUEGO "Preparados... listos.. yaaaaaaaaa!"
#define MENSAJE_GANADOR "WOAAAW!! GANASTE :D"
#define MENSAJE_PERDEDOR_CON_GANADOR "GANADOR: "
#define MENSAJE_PERDEDOR_SIN_GANADOR "Ups... GAME OVER"

#define ESPACIO_ENTRE_JUGADORES 20

ControladorConexiones::ControladorConexiones(int cantidadJugadores) {
	
	log_controlador.open(LOG_CONTROLADOR);
	log_controlador << "Abierto log controlador\n" << flush;
	
	this->cantidadJugadores = cantidadJugadores;
	
	for (unsigned int i=0; i<GestionTiposPersonaje::getListaTipos().size(); i++) {
		personajesHabilitados.push_back(true);
	}
	
	pthread_mutex_init(&mutexConexiones, NULL);
	pthread_mutex_init(&mutexEstado, NULL);
	pthread_mutex_init(&mutexPersonajesHabilitados, NULL);
	
	setEstadoJuego(JuegoNoComenzo);
	
	log_controlador << "Controlador inicializado correctamente\n" << flush;
	
}

ControladorConexiones::~ControladorConexiones() {
	
	log_controlador << "Eliminando controlador de conexiones\n" << flush;
	log_controlador.close();
	
	pthread_mutex_destroy(&mutexConexiones);
	pthread_mutex_destroy(&mutexEstado);
	pthread_mutex_destroy(&mutexPersonajesHabilitados);
	
}

void ControladorConexiones::ComenzarControl(void* datos, void* ManejoConexiones(void*)) {
	
	if (pthread_create (&thread, NULL, ManejoConexiones, datos) != 0) {
		log_controlador << "No se puede crear el thread principal\n";
		exit(1);
	}
}

void ControladorConexiones::ComenzarJuego() {
	
	int posicionX = 0;
	pthread_mutex_lock(&mutexConexiones);
	for (unsigned int i=0; i< conexiones.size(); i++) {
		conexiones.at(i)->getControladorEscritura()->addUpdate(MENSAJE_INICIO_JUEGO);
		conexiones.at(i)->getControladorLectura()->getControlador()->getJugador()->setPosicion(posicionX, conexiones.at(i)->getControladorLectura()->getControlador()->getJugador()->getY());
		conexiones.at(i)->getControladorLectura()->getControlador()->getJugador()->setPosicionInicial(posicionX, conexiones.at(i)->getControladorLectura()->getControlador()->getJugador()->getY());
		posicionX += conexiones.at(i)->getControladorLectura()->getControlador()->getJugador()->getAncho() + ESPACIO_ENTRE_JUGADORES;
	}
	pthread_mutex_unlock(&mutexConexiones);
	
	setEstadoJuego(Jugando);
	
	log_controlador << "Juego comenzado\n" << flush;
}

void ControladorConexiones::ManejarEstados() {
	
	bool proximoJuego = true;
	vector<ControladorCliente*> conexionesADesactivar;
	
	if (getEstadoJuego() == JuegoNoComenzo) {
		pthread_mutex_lock(&mutexConexiones);
		
		for (unsigned int i=0; i< conexiones.size(); i++) {
			if (conexiones.at(i)->getEstadoJuego() == EsperandoJugadores) {
				conexiones.at(i)->getControladorEscritura()->addUpdate(MENSAJE_ESPERANDO_JUGADORES);
			}
			else if (conexiones.at(i)->getControladorLectura()->getEstadoLectura() != LecturaCargando) {
				conexiones.at(i)->setEstadoJuego(EsperandoJugadores);
			}
		}
		pthread_mutex_unlock(&mutexConexiones);
	}
	
	if (conexiones.size() == 0) return;
	
	pthread_mutex_lock(&mutexConexiones);
	for (unsigned int i=0; i<conexiones.size(); i++) {
		if (conexiones.at(i)->getControladorEscritura()->getEstadoEscritura() == EscrituraFin &&
			conexiones.at(i)->getControladorLectura()->getEstadoLectura() == LecturaFin
			&& conexiones.at(i)->getEstadoJuego() != FinJuego)
		{
			conexiones.at(i)->EnviarMensaje(MENSAJE_PERDEDOR_SIN_GANADOR, "F");
			log_controlador << "Mensaje fin a una conexion\n" << flush;
			log_controlador << "Proximo juego a una conexion\n" << flush;
			conexiones.at(i)->setEstadoJuego(ProximoJuego);
		}
		
		if (conexiones.at(i)->getEstadoJuego() == ProximoJuego) {
			log_controlador << "Una conexion esta lista para terminar\n" << flush;
			conexionesADesactivar.push_back(conexiones.at(i));
			continue;
		}

		if (conexiones.at(i)->getControladorEscritura()->getEstadoEscritura() == EscrituraInactiva &&
			conexiones.at(i)->getControladorLectura()->getEstadoLectura() == LecturaInactiva) {
			log_controlador << "Una conexion esta inactiva\n" << flush;
			conexionesADesactivar.push_back(conexiones.at(i));
			continue;
		}
		
		proximoJuego = false;
		
		if (conexiones.at(i)->getControladorEscritura()->getEstadoEscritura() == EscrituraFin &&
			conexiones.at(i)->getControladorLectura()->getEstadoLectura() == LecturaFin
			&& conexiones.at(i)->getEstadoJuego() == FinJuego) {
			
			if (conexiones.at(i)->getControladorLectura()->getControlador()->JugadorGano()) {
				conexiones.at(i)->EnviarMensaje(MENSAJE_GANADOR, "F");
			} else {
				conexiones.at(i)->EnviarMensaje(MENSAJE_PERDEDOR_CON_GANADOR + nombreGanador, "F");
			}
			conexiones.at(i)->setEstadoJuego(ProximoJuego);
		}
		
		if (conexiones.at(i)->getEstadoJuego() == JuegoNoComenzo ||
			conexiones.at(i)->getEstadoJuego() == Jugando ||
			conexiones.at(i)->getEstadoJuego() == FinJuego) {
			conexiones.at(i)->setEstadoJuego(estado);
			continue;
		}
		
		if (conexiones.at(i)->getEstadoJuego() == EsperandoJugadores && estado != JuegoNoComenzo) {
			conexiones.at(i)->setEstadoJuego(estado);
		}
		
	}
	pthread_mutex_unlock(&mutexConexiones);
	
	for (unsigned int i=0; i<conexionesADesactivar.size(); i++) {
		DesactivarConexion(conexionesADesactivar.at(i));
	}
	
	if (proximoJuego) {
		setEstadoJuego(ProximoJuego);
	}
			
}

int ControladorConexiones::getCantidadJugadores() {
	
	return cantidadJugadores;
}

void ControladorConexiones::setNombreGanador(string nombre) {	
	nombreGanador = nombre;
}

bool ControladorConexiones::ListoParaEmpezar() {
	
	pthread_mutex_lock(&mutexConexiones);
	if (getCantidadJugadores() != conexiones.size()) {
		pthread_mutex_unlock(&mutexConexiones);
		return false;
	}
	
	for (unsigned int i=0; i<conexiones.size(); i++) {
		
		if (conexiones.at(i)->getEstadoJuego() != EsperandoJugadores) {
			pthread_mutex_unlock(&mutexConexiones);
			return false;
		}
	}
	
	pthread_mutex_unlock(&mutexConexiones);
	log_controlador << "Juego listo para empezar\n" << flush;
	return true;
}

bool ControladorConexiones::AgregarConexion(ControladorCliente* conexion) {
	
	if (getEstadoJuego() != JuegoNoComenzo) return false;
	
	pthread_mutex_lock(&mutexConexiones);
	if (getCantidadJugadores() > conexiones.size())
		conexiones.push_back(conexion);
	else {
		pthread_mutex_unlock(&mutexConexiones);
		return false;
	}
	
	int tipoPersonaje = conexion->getControladorLectura()->getControlador()->getTipoPersonaje();
	string personaje = GestionTiposPersonaje::getListaTipos().at(tipoPersonaje);
	string mensaje = "Personaje " + personaje + MENSAJE_INGRESO;
	for (unsigned int i=0; i< conexiones.size(); i++)
		if (conexiones.at(i)->getControladorLectura()->getEstadoLectura() != LecturaCargando)
			conexiones.at(i)->getControladorEscritura()->addUpdate(mensaje);
	pthread_mutex_unlock(&mutexConexiones);
	
	pthread_mutex_lock(&mutexEstado);
	conexion->setEstadoJuego(estado);
	pthread_mutex_unlock(&mutexEstado);
	
	log_controlador << "Agregada una conexion" << endl << flush;
	log_controlador << "La cantidad de clientes ahora es " << CantidadConexiones() << endl << flush;
	
	return true;
}

void ControladorConexiones::DesactivarConexion(ControladorCliente* conexion) {
	
	pthread_mutex_lock(&mutexConexiones);
	
	int indice = find(conexiones.begin(), conexiones.end(), conexion) - conexiones.begin();
	shutdown(conexiones.at(indice)->getSocket(), 2);
	close(conexiones.at(indice)->getSocket());
	
	conexion->getControladorLectura()->LockJugador();
	conexiones.erase(conexiones.begin() + indice);
	conexion->getControladorLectura()->UnlockJugador();
	
	int tipoPersonaje = conexion->getControladorLectura()->getControlador()->getTipoPersonaje();
	string personaje = GestionTiposPersonaje::getListaTipos().at(tipoPersonaje);
	string mensaje = "Personaje " + personaje + MENSAJE_ABANDONO;
	
	for (unsigned int i=0; i< conexiones.size(); i++)
		if (conexiones.at(i)->getControladorLectura()->getEstadoLectura() != LecturaCargando)
			conexiones.at(i)->getControladorEscritura()->addUpdate(mensaje);
		
	pthread_mutex_unlock(&mutexConexiones);
	
	delete conexion;
	
	DesocuparPersonaje(tipoPersonaje);
	
	log_controlador << "Eliminada una conexion" << endl << flush;
	log_controlador << "La cantidad de clientes ahora es " << CantidadConexiones() << endl << flush;
}

unsigned int ControladorConexiones::CantidadConexiones() {
	
	pthread_mutex_lock(&mutexConexiones);
	unsigned int cantidad = conexiones.size();
	pthread_mutex_unlock(&mutexConexiones);
	
	return cantidad;
}
	
EstadoJuego ControladorConexiones::getEstadoJuego() {
	
	pthread_mutex_lock(&mutexEstado);
	EstadoJuego e = estado;
	pthread_mutex_unlock(&mutexEstado);
	
	return e;
}

void ControladorConexiones::setEstadoJuego(EstadoJuego estado) {
	
	pthread_mutex_lock(&mutexEstado);
	this->estado = estado;
	pthread_mutex_unlock(&mutexEstado);
}

pthread_t ControladorConexiones::getThread() {
	
	return thread;
}

vector< pair<int, TipoPersonaje*> > ControladorConexiones::InfoPersonajesHabilitados() {
	
	pthread_mutex_lock(&mutexPersonajesHabilitados);
	vector<bool> pH = personajesHabilitados;
	pthread_mutex_unlock(&mutexPersonajesHabilitados);
	
	vector< pair<int, TipoPersonaje*> > personajes;
	for (unsigned int i=0; i< pH.size(); i++) {
		
		if (pH.at(i)) {
			pair<int, TipoPersonaje*> personaje(i, GestionTiposPersonaje::getTipoPersonaje(i));
			personajes.push_back(personaje);
		}
	}
	
	return personajes;
}

bool ControladorConexiones::OcuparPersonaje(int tipoPersonaje) {
	
	bool habilitado = true;
	
	pthread_mutex_lock(&mutexPersonajesHabilitados);
	if (personajesHabilitados.at(tipoPersonaje))
		personajesHabilitados.at(tipoPersonaje) = false;
	else habilitado = false;
	pthread_mutex_unlock(&mutexPersonajesHabilitados);
	
	return habilitado;
}

void ControladorConexiones::DesocuparPersonaje(int tipoPersonaje) {
	
	pthread_mutex_lock(&mutexPersonajesHabilitados);
	personajesHabilitados.at(tipoPersonaje) = true;
	pthread_mutex_unlock(&mutexPersonajesHabilitados);
}

void ControladorConexiones::LockConexiones() {
	
	pthread_mutex_lock(&mutexConexiones);
}

void ControladorConexiones::UnlockConexiones() {
	
	pthread_mutex_unlock(&mutexConexiones);
}

vector<Personaje*> ControladorConexiones::getPersonajes() {
	
	vector<Personaje*> personajes;
	for (unsigned int i=0; i<conexiones.size(); i++) {		
		personajes.push_back(conexiones.at(i)->getControladorLectura()->getControlador()->getJugador());
	}
	
	return personajes;
}

void ControladorConexiones::LockPersonajes() {
	
	for (unsigned int i=0; i<conexiones.size(); i++) {
		conexiones.at(i)->getControladorLectura()->LockJugador();
	}
}

void ControladorConexiones::UnlockPersonajes() {
	
	for (unsigned int i=0; i<conexiones.size(); i++) {
		conexiones.at(i)->getControladorLectura()->UnlockJugador();
	}
}

void ControladorConexiones::setPaquetes(string mensajePersonajes, string mensajeObjetos, string mensajePuntajes) {

	for (unsigned int i=0; i<conexiones.size(); i++) {
		
		string mensajePosicion = mensajePersonajes.substr(0, mensajePersonajes.find_first_of("#")+1);
		mensajePersonajes.erase(0, mensajePersonajes.find_first_of("#")+1);
		mensajePersonajes += mensajePosicion;
		string paquete = "<" + mensajeObjetos + mensajePersonajes + ">";
		conexiones.at(i)->getControladorEscritura()->setDibujables(paquete);
		
		string mensajePuntaje = mensajePuntajes.substr(0, mensajePuntajes.find_first_of(">")+1);
		mensajePuntajes.erase(0, mensajePuntajes.find_first_of(">")+1);
		conexiones.at(i)->getControladorEscritura()->setPuntajes(mensajePuntaje);
	}
	
	CongelarPosiciones();
	
}

void ControladorConexiones::setSonidos(string mensajeSonidos) {

	for (unsigned int i=0; i<conexiones.size(); i++) {
		conexiones.at(i)->getControladorEscritura()->setSonidos(mensajeSonidos);
	}
}

void ControladorConexiones::CongelarPosiciones() {
	
	for (unsigned int i=0; i<conexiones.size(); i++) {
		conexiones.at(i)->CongelarPosicionJugador();
	}
}
		
